Golang源码-unsafe

Unsafe包很有意思,源码中短短几行代码,剩下更多的是注释,初略一看感觉并不重要,其实恰恰相反,很多package中都有它的影子,比如reflect

先从包名讲起,unsafe - 不安全,为什么不安全呢? Package unsafe contains operations that step around the type safety of Go programs

  • A pointer value of any type can be converted to a Pointer.
  • A Pointer can be converted to a pointer value of any type.
  • A uintptr can be converted to a Pointer.
  • A Pointer can be converted to a uintptr.

简单来说就是

指针 <—> unsafe.Pointer <—> unitptr

例子 - 强制转换类型

type stringHeader struct {
    Data unsafe.Pointer
    Len  int
}

var s stinrg = abc"

// *string -> unsafe.Pointer -> *stringHeader
hdr := (*stringHeader)(unsafe.Pointer(&s))

println(&s) // 0xc42000e260
println(&hdr.Data) // 0xc42000e260
println(&hdr.Len) // 0xc42000e268
hdr.Data = unsafe.Pointer(&b[3])
hdr.Len = int(b[1])<<8 | int(b[2])

例子 - 通过位置获取值

// 数组
func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
    return unsafe.Pointer(uintptr(p) + x)
}

func main() {
    ints := []int64{1, 22, 123} // 数组
    println(&ints[2]) // 第三个成员的地址 0xc42003bf70
    up := add(unsafe.Pointer(&ints[0]), 2*unsafe.Sizeof(ints[0])) // 根据成员的size,移动Pointer到目标位置
    println((*int)(up)) // 通过add方法移动到的地址 0xc42003bf70
    println(*(*int)(up)) // 地址对应的值
}

最后一个例子 - 通过指针强制转换和移动位置修改私有成员

文件夹 pointer 中定义一个P的类,P的成员,i和s,都是私有类型,所以只能查看(GetI, GetS公有)而不能修改

package pointer

type P struct {
    i int32
    s string
}

func (p *P) GetI() int32 {
    return p.i
}

func (p *P) GetS() string {
    return p.s
}

然后在包外写一个main文件

package main

import (
    "pointer"
)

func main() {
    p := new(pointer.P)
    println(p.GetI())
    println(p.GetS())

    // 修改
    pointI := (*int32)(unsafe.Pointer(p))
    *pointI = 20
    println(p.GetI())

    pointS := (*string)(add(unsafe.Pointer(p), uintptr(8)))
    *pointS = "我就是我"
    println(p.GetS())
}
Nevermore Written by:

步步生姿,空锁满庭花雨。胜将娇花比。